﻿using System;
using System.IO;
using System.Threading;
using DigitClassification.Properties;

namespace DigitClassification
{
    class Program
    {
        private static string trainData;
        private static string trainLabels;
        private static string valData;
        private static string valLabels;
        private static string testData;
        private static string testLabels;

        static void Main(string[] args)
        {
            InitDataFiles();

            ClassifyNaiveBayes();
            ClassifyPerceptron();

            RemoveDataFiles();
        }

        private static void ClassifyNaiveBayes()
        {
            var nbClass = new NaiveBayesClassifier(trainData, trainLabels);

            double minErrorRate = 101;
            int bestK = -1;

            Console.WriteLine(@"");
            Console.WriteLine(@"Answer for Question 1:");
            Console.WriteLine(@"=======================");
            Console.WriteLine(@"Answer for Question 1.1:");
            for (int k = 1; k <= 10; k++)
            {
                var errorRate = nbClass.Classify("validation", valData, valLabels, k);
                if (errorRate < minErrorRate)
                {
                    minErrorRate = errorRate;
                    bestK = k;
                }
            }
            Console.WriteLine(@"Best K-Smoothing is: {0} , which result with error rate: {1}", bestK, minErrorRate);

            Console.WriteLine(@"");
            Console.WriteLine(@"Answer for Question 1.2:");
            nbClass.Classify("test", testData, testLabels, bestK);

            Console.WriteLine(@"");
            Console.WriteLine(@"Answer for Question 1.3:");
            nbClass.Classify("training", trainData, trainLabels, bestK);
            Console.WriteLine(@"It's not zero because evaluation process made it less-overfit to training.");
            Console.WriteLine(@"It's better than test performance, because probability is still based on training a with little smoothing.");
        }

        private static void ClassifyPerceptron()
        {
            var perClass = new PerceptronClassifier(trainData, trainLabels);
            var epochsLimit = 3;

            Console.WriteLine(@"");
            Console.WriteLine(@"Answer for Question 2:");
            Console.WriteLine(@"=======================");
            Console.WriteLine(@"Answer for Question 2.1:");
            perClass.ReCalcWeights(epochsLimit);
            perClass.Classify("test",testData, testLabels);
            perClass.Classify("training",trainData, trainLabels);

            Console.WriteLine(@"");
            Console.WriteLine(@"Answer for Question 2.2:");
            perClass.ReCalcWeights(0);
            for (int i = 1; i <= 6; i++)
            {
                perClass.RunEpoch();
                perClass.Classify("test", testData, testLabels);
            }

            Console.WriteLine(@"");
            Console.WriteLine(@"Answer for Question 2.3:");
            Console.WriteLine(@"We would prefer using Perceptron using 4 iterations");
            Console.WriteLine(@"Perceptron error rate: 17.2% ");
            Console.WriteLine(@"NaiveBayes error rate: 18.2%");
            Console.WriteLine(@"The difference is only 1%");
            Console.WriteLine(@"The reasons can be:");
            Console.WriteLine(@" Perceptron distinghish between similar classes by giving a 'penalty' to the wrong-class and a 'bonus' to the correct-class");

            Console.ReadKey();
        }

        private static void InitDataFiles()
        {
            trainData = Path.GetTempFileName();
            File.WriteAllBytes(trainData, Resources.trainingimages);

            trainLabels = Path.GetTempFileName();
            File.WriteAllBytes(trainLabels, Resources.traininglabels);

            valData = Path.GetTempFileName();
            File.WriteAllBytes(valData, Resources.validationimages);

            valLabels = Path.GetTempFileName();
            File.WriteAllBytes(valLabels, Resources.validationlabels);

            testData = Path.GetTempFileName();
            File.WriteAllBytes(testData, Resources.testimages);

            testLabels = Path.GetTempFileName();
            File.WriteAllBytes(testLabels, Resources.testlabels);
        }

        private static void RemoveDataFiles()
        {
            try
            {
                File.Delete(trainData);
                File.Delete(trainLabels);
                File.Delete(valData);
                File.Delete(valLabels);
                File.Delete(testData);
                File.Delete(testLabels);
            }
            catch (Exception ex)
            {
            }
        }
    }
}
